package com.chenl.consumer.util;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 分布式锁
 * /
 */
@Component
public class RedisLock {
    @Resource
    private RedisTemplate stringRedisTemplate;
    private static final String KEY_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString().replace("-", "") + "-";
    private static final DefaultRedisScript<Long> UNLOCK_SCRIPT;
    ThreadLocal<String> threadLocal = new InheritableThreadLocal();

    static {
        UNLOCK_SCRIPT = new DefaultRedisScript<>(
                "if(redis.call)('get',KEYS[1]) == ARGV[1] then\n"
                        + "return redis.call('del',KEYS[1])\n"
                        + "end\n"
                        + "return 0", Long.class);
    }

    //获取锁(手动释放)
    public Boolean tryLock(String name, Long time, TimeUnit timeUnit) {
        //获取线程标识
        String threadId = UUID.randomUUID().toString().replace("-", "") + "-" + Thread.currentThread().getId();
        threadLocal.set(threadId);
        //获取锁
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId,time,timeUnit);
        return Boolean.TRUE.equals(flag);
    }

    //获取锁(自动释放)
    public Boolean tryLockAutoRelease(String name, Long time, TimeUnit timeUnit) {
        //获取线程标识
        String threadId = ID_PREFIX + Thread.currentThread().getId();
        //获取锁
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId,time,timeUnit);
        return Boolean.TRUE.equals(flag);
    }

    public void unlock(String name) {
        //调用lua脚本
        stringRedisTemplate.execute(UNLOCK_SCRIPT,
                Collections.singletonList(KEY_PREFIX + name),
                threadLocal.get());
        threadLocal.remove();
    }

    //重试获取锁(手动释放),reTryCount为重试次数,reTryinterval为重试间隔毫秒数 -
    public Boolean tryLock(String name, Long time, TimeUnit timeUnit, Integer reTryCount, Long reTryinterval) throws InterruptedException {
        //获取线程标识
        String threadId = UUID.randomUUID().toString().replace("-", "") + "-" + Thread.currentThread().getId();
        //获取锁
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, time, timeUnit);
        int count = 0;
        while (!flag && count < reTryCount) {
            Thread.sleep(reTryinterval);
            flag = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId, time, timeUnit);
            count++;
        }
        if(flag){
            threadLocal.set(threadId);
        }
        return Boolean.TRUE.equals(flag);
    }
}
